#! /usr/bin/env python

import os, re, wave, io, math, struct
import waflib.Task, waflib.TaskGen, waflib.Options
from waf_dynamo import copy_file_task
from waflib.TaskGen import extension, extension
from BuildUtility import create_build_utility
from build_constants import TargetOS

def gen_tone(task):
    tone_freq = int(task.generator.tone)
    sample_freq = int(task.generator.rate)
    sample_count = int(task.generator.frames)
    channels = int(task.generator.channels)
    ramp = bool(task.generator.ramp)

    f = wave.open(task.outputs[0].abspath(), "wb")
    f.setnchannels(channels)
    f.setsampwidth(2)
    f.setframerate(sample_freq)
    buf = io.BytesIO()

    frames = []
    for i in range(sample_count):
        a = 0.8 * 32768 * math.sin((i * 2.0 * math.pi * tone_freq) / sample_freq)
        if ramp:
            r = ((sample_count - 1) - i) / float(sample_count)
            a = a * r
        if channels == 1:
            buf.write(struct.pack('h', int(a)))
        else:
            buf.write(struct.pack('hh', int(a), int(a)))
    f.writeframes(buf.getvalue())
    f.close()

    return 0

def gen_dc(task):
    sample_freq = int(task.generator.rate)
    sample_count = int(task.generator.frames)
    channels = int(task.generator.channels)

    f = wave.open(task.outputs[0].abspath(), "wb")
    f.setnchannels(channels)
    f.setsampwidth(2)
    f.setframerate(sample_freq)
    buf = io.BytesIO()

    frames = []
    for i in range(sample_count):
        a = 0.8 * 32768
        if channels == 1:
            buf.write(struct.pack('h', int(a)))
        else:
            buf.write(struct.pack('hh', int(a), int(a)))
    f.writeframes(buf.getvalue())
    f.close()

    return 0

def build(bld):
    if waflib.Options.options.skip_build_tests:
        return

    build_util = create_build_utility(bld.env)
    target_os = build_util.get_target_os()

    platform = bld.env['PLATFORM']

    if 'web' in platform:
        lib_dirs = {}
        lib_dirs['library_sound.js'] = '../src/js'
        bld.env['JS_LIB_PATHS'] = lib_dirs

    wavs = ["booster_on_sfx.wav",
            "door_opening.wav",
            "drumloop.wav",
            "onefootstep.wav",
            "osc2_sin_440hz.wav",
            "def2938.wav",
            "music_adpcm.wav",
            "mono_resample_framecount_16000_adpcm.wav",
            "ambience_adpcm.wav"]

    for tone in [440, 2000]:
        for rate in [22050, 32000, 44000, 44100, 48000]:
            for channels in [1, 2]:
                for length in  [0.25, 2.0]: # seconds
                    frames = length * rate
                    name = '%s_tone_%d_%d_%d.wav' % (["mono", "stereo"][channels-1], tone, rate, frames)
                    wavs.append(name)
                    bld(target = name,
                        tone = tone,
                        rate = rate,
                        frames = frames,
                        channels = channels,
                        ramp = False,
                        rule = gen_tone)

    for rate in [32000]:
        for tone in [440]:
            channels = 1
            frames = 2 * rate
            name = '%s_toneramp_%d_%d_%d.wav' % (["mono", "stereo"][channels-1], tone, rate, frames)
            wavs.append(name)
            bld(target = name,
                tone = tone,
                rate = rate,
                frames = frames,
                channels = channels,
                ramp = True,
                rule = gen_tone)

    for rate in [44100]:
        channels = 1
        frames = 2 * rate
        name = '%s_dc_%d_%d.wav' % (["mono", "stereo"][channels-1], rate, frames)
        wavs.append(name)
        bld(target = name,
            rate = rate,
            frames = frames,
            channels = channels,
            ramp = True,
            rule = gen_dc)

    bld.add_group()

    embedded_wavs = bld.stlib(features = 'cxx embed test',
                                     target = 'embedded_wavs',
                                     source = 'dummy.cpp',  # Make waf happy
                                     embed_source = wavs)
    embedded_oggs = bld.stlib(features = 'cxx embed test',
                                     target = 'embedded_oggs',
                                     source = 'dummy.cpp',  # Make waf happy
                                     embed_source = [x.name for x in bld.path.ant_glob('**/*.ogg')])
    embedded_opus = bld.stlib(features = 'cxx embed test',
                                     target = 'embedded_opus',
                                     source = 'dummy.cpp',  # Make waf happy
                                     embed_source = [x.name for x in bld.path.ant_glob('**/*.opus')])

    soundlibs = ['SOUND', 'OPUS', 'decoder_wav', 'decoder_ogg', 'decoder_opus']
    exported_symbols = ["DefaultSoundDevice", "AudioDecoderWav", "AudioDecoderStbVorbis", "AudioDecoderOpus"]

    if bld.env.PLATFORM not in ['js-web', 'wasm-web', 'wasm_pthread-web', 'win32', 'x86_64-win32', 'arm64-nx64', 'x86_64-ps4', 'x86_64-ps5']:
        exported_symbols.append("AudioDecoderTremolo")
        soundlibs.append('TREMOLO')

    if bld.env.PLATFORM in ['arm64-nx64', 'x86_64-ps4']:
        pass
    else:
        if bld.env.PLATFORM not in ('x86_64-macos','arm64-macos','x86_64-ios','arm64-ios'):
            soundlibs.append('OPENAL')
        soundlibs.append('DMGLFW')
        soundlibs.append('OPENGL')

    if 'TREMOLO' in soundlibs:
        bld.program(features = 'cxx embed test',
                    includes = '../../src .',
                    use = 'TESTMAIN DLIB SOCKET PROFILE_NULL sound embedded_wavs embedded_oggs embedded_opus'.split() + soundlibs,
                    web_libs = ['library_sound.js'],
                    exported_symbols = exported_symbols,
                    target = 'test_sound_perf',
                    source = 'test_sound_perf.cpp')

    extra_features = []
    if waflib.Options.options.with_asan and bld.env.PLATFORM in ['win32', 'x86_64-win32']:
        extra_features = ['skip_test']
        print("Skipping sound test since current version throws an error with ASan")

    test = bld.program(features = 'cxx embed test'.split() + extra_features,
                includes = '../../src .',
                use = 'TESTMAIN DLIB SOCKET PROFILE_NULL sound embedded_wavs embedded_oggs embedded_opus'.split() + soundlibs,
                web_libs = ['library_sys.js', 'library_sound.js'],
                exported_symbols = exported_symbols + ['LoopBackDevice'],
                target = 'test_sound',
                source = 'test_sound.cpp')

    if bld.env['PLATFORM'] in ('wasm-web', 'wasm_pthread-web'):
        for f in ['CFLAGS', 'CXXFLAGS']:
            test.env.append_value(f, ['-msimd128', '-msse4.2', '-DDM_SOUND_DSP_IMPL=WASM'])

    # making sure we test link the openal backend
    if target_os in [TargetOS.LINUX]:
        test_sound_libs = [x for x in test.use if x != 'sound'] # keep the libraries in sync
        bld.program(features = 'cxx embed test skip_test',
                        includes = '../../src .',
                        use = ['sound_openal'] + test_sound_libs,
                        defines = ['DM_TEST_SOUND_USE_OPENAL'],
                        exported_symbols = exported_symbols,
                        target = 'test_sound_openal',
                        source = 'test_sound.cpp')

    # test that the linkage doesn't break again
    exported_symbols = 'NullSoundDevice TestNullDevice'
    bld.program(features = 'cxx embed test',
                    includes = '../../src .',
                    use = 'TESTMAIN DLIB SOCKET PROFILE_NULL sound_null embedded_wavs embedded_oggs embedded_opus',
                    web_libs = ['library_sound.js'],
                    exported_symbols = exported_symbols,
                    target = 'test_sound_null',
                    source = 'test_sound_null.cpp')
